package io.gitee.thinkbungee.crud.mybatis.service;

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import io.gitee.thinkbungee.constant.SatelliteConstant;
import io.gitee.thinkbungee.crud.BaseEntity;
import io.gitee.thinkbungee.crud.PageParam;
import io.gitee.thinkbungee.crud.annotation.Query;
import io.gitee.thinkbungee.crud.mybatis.annotation.UnionUnique;
import io.gitee.thinkbungee.crud.mybatis.annotation.Unique;
import io.gitee.thinkbungee.crud.mybatis.entity.SatelliteMybatisEntity;
import io.gitee.thinkbungee.crud.mybatis.mapper.SatelliteMybatisMapper;
import io.gitee.thinkbungee.enums.BaseOperationEnum;
import io.gitee.thinkbungee.exception.BusinessException;
import io.gitee.thinkbungee.utils.SatelliteUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * mybatis基础层service
 *
 * @author chenyichang
 * @date 2022/2/17
 */
public interface SatelliteMybatisBaseService<P extends PageParam, T extends BaseEntity> {

    Logger log = LoggerFactory.getLogger(SatelliteMybatisBaseService.class);

    //=====================必须由子类实现===========================//

    /**
     * 获取mapper
     *
     * @return
     */
    SatelliteMybatisMapper<T> getMapper();

    //======================拦截器，操作前、后的处理======================//

    /**
     * 操作前处理
     *
     * @param entity    实体对象
     * @param operation 具体操作类型
     * @throws BusinessException 异常，回滚事务
     */
    default void processBeforeOperation(T entity, BaseOperationEnum operation) throws BusinessException {

    }

    /**
     * 操作前处理
     *
     * @param list      实体对象
     * @param operation 具体操作类型
     * @throws BusinessException 异常，回滚事务
     */
    default void processBatchBeforeOperation(List<T> list, BaseOperationEnum operation) throws BusinessException {

    }


    /**
     * 操作后处理
     *
     * @param entity    实体
     * @param operation 具体操作类型
     * @throws BusinessException 异常，回滚事务
     */
    default void processAfterOperation(T entity, BaseOperationEnum operation) throws BusinessException {

    }

    /**
     * 操作后处理
     *
     * @param list      实体
     * @param operation 具体操作类型
     * @throws BusinessException 异常，回滚事务
     */
    default void processBatchAfterOperation(List<T> list, BaseOperationEnum operation) throws BusinessException {

    }


    //=====================包装实体及扩展处理========================//

    /**
     * 包装返回实体
     *
     * @param entity
     * @return
     */
    default T wrapperEntity(T entity) {
        return entity;
    }

    /**
     * 结果处理
     *
     * @param iPage 结果
     * @return
     */
    default IPage<T> resultHandler(IPage<T> iPage) {
        return iPage;
    }

    /**
     * 扩展查询条件
     *
     * @param param   查询参数
     * @param wrapper 查询条件
     * @return
     */
    default Wrapper<T> extensionWrapper(P param, QueryWrapper<T> wrapper) {
        return wrapper;
    }

    /**
     * 对查询条件进行处理
     *
     * @param queryWrapper 查询条件
     * @return
     */
    default Wrapper<T> handlerQueryWrapper(Wrapper<T> queryWrapper) {
        return queryWrapper;
    }

    //====================业务相关============================//

    /**
     * 根据参数拼装查询条件
     *
     * @param param 参数
     * @return
     */
    default QueryWrapper<T> getWrapper(P param) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        //解析param==>queryWrapper
        initWrapper(param, queryWrapper);
        return queryWrapper;
    }

    /**
     * 将param的查询条件解析到queryWrapper中
     *
     * @param param        查询条件
     * @param queryWrapper 封装的查询条件
     */
    default void initWrapper(P param, QueryWrapper<T> queryWrapper) {
        //拿到param的字段查询条件
        Field[] fields = param.getClass().getDeclaredFields();

        //Query注解,没有注解的字段不参与条件
        Arrays.stream(fields).filter(field -> {
            if (field.isAnnotationPresent(Query.class)) {
                Query query = field.getAnnotation(Query.class);
                return query.where();
            }
            return false;
        }).forEach(field -> {
            //必定有 Query注解
            try {
                boolean flag;
                field.setAccessible(true);
                //判断该属性的值是否为null 或者 空 " "
                if (field.get(param) instanceof String) {
                    flag = StringUtils.isNoneBlank((String) field.get(param));
                } else {
                    flag = field.get(param) != null;
                }
                //字段为空，没啥比较的，跳过
                if (!flag) {
                    return;
                }

                //取查询字段,优先取注解上的column值，否则按照驼峰命名转下划线取字段名
                String column =
                        StringUtils.isNotBlank(field.getAnnotation(Query.class).column()) ?
                                field.getAnnotation(Query.class).column() :
                                SatelliteUtils.camelCaseToUnderline(field.getName());

                //根据注解，来拼装查询条件
                switch (field.getAnnotation(Query.class).value()) {
                    //EQ 写到默认中
//                    case EQ:
//                        break;
                    case LIKE:
                        String like = String.valueOf(field.get(param));
                        //特殊字符转义
                        if (like.contains("%")) {
                            like = like.replace("%", "\\%");
                        }
                        if (like.contains("_")) {
                            like = like.replace("_", "\\_");
                        }
                        queryWrapper.like(column, like);
                        break;
                    case GT:
                        queryWrapper.gt(column, field.get(param));
                        break;
                    case GTE:
                        queryWrapper.ge(column, field.get(param));
                        break;
                    case LT:
                        queryWrapper.lt(column, field.get(param));
                        break;
                    case LTE:
                        queryWrapper.le(column, field.get(param));
                        break;
                    case IN:
                        Object value = field.get(param);
                        if (value instanceof List) {
                            queryWrapper.in(column, (List) value);
                        } else if (value instanceof String) {
                            String[] splits = ((String) value).split(SatelliteConstant.SPLIT);
                            queryWrapper.in(column, Arrays.asList(splits));
                        }
                        break;
                    case BETWEEN:
                        String[] split = String.valueOf(field.get(param)).split(SatelliteConstant.SPLIT);
                        if (split.length == 2) {
                            queryWrapper.between(column, split[0], split[1]);
                        } else if (split.length == 1) {
                            queryWrapper.ge(column, split[0]);
                        }
                        break;
                    default:
                        //默认为等于
                        queryWrapper.eq(column, field.get(param));
                }

            } catch (Exception e) {
                log.error("initWrapper error", e);
            }
        });
    }


    //===============提供一些基础的增删改查========================//

    /**
     * 新增数据
     *
     * @param entity 实体
     * @return 是否成功
     * @throws BusinessException 异常
     */
    @Transactional(rollbackFor = Exception.class)
    default Integer insert(T entity) throws BusinessException {
        //处理前
        processBeforeOperation(entity, BaseOperationEnum.INSERT);

        //检验索引
        checkUniqueField(entity, false);

        Integer result = getMapper().insert(entity);

        if (result == null || result < 1) {
            throw BusinessException.build(SatelliteConstant.Exception.INSERT_FAILURE);
        }

        //缓存
        refreshCacheFields(entity, BaseOperationEnum.INSERT);

        //处理后
        processAfterOperation(entity, BaseOperationEnum.INSERT);

        return result;
    }


    /**
     * 批量新增
     *
     * @param entities 数据
     * @return
     * @throws BusinessException 异常
     */
    @Transactional(rollbackFor = Exception.class)
    default Integer insertBatch(List<T> entities) throws BusinessException {
        Integer result = getMapper().insertBatch(entities);

        //刷新缓存
        entities.forEach(entity -> refreshCacheFields(entity, BaseOperationEnum.INSERT));

        if (result == null || result < 1) {
            throw BusinessException.build(SatelliteConstant.Exception.INSERT_FAILURE);
        }

        return result;
    }

    /**
     * 更新数据
     *
     * @param entity 实体
     * @return
     * @throws BusinessException 异常
     */
    @Transactional(rollbackFor = Exception.class)
    default Integer update(T entity) throws BusinessException {
        //更新前操作
        processBeforeOperation(entity, BaseOperationEnum.UPDATE);
        //检查唯一索引
        checkUniqueField(entity, true);

        //先删除缓存
        if (entity instanceof SatelliteMybatisEntity) {
            SatelliteMybatisEntity sEntity = (SatelliteMybatisEntity) entity;
            T dbEntity = selectById(sEntity.getId());
            refreshCacheFields(dbEntity, BaseOperationEnum.DELETE);
        }
        //更新
        Integer result = getMapper().updateById(entity);
        if (result == null || result < 1) {
            throw BusinessException.build(SatelliteConstant.Exception.UPDATE_FAILURE);
        }

        //入缓存
        refreshCacheFields(entity, BaseOperationEnum.UPDATE);

        //更新后操作
        processAfterOperation(entity, BaseOperationEnum.UPDATE);
        return result;
    }

    /**
     * 删除
     *
     * @param id 主键id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    default Integer deleteById(Serializable id) {
        T t = selectById(id);
        if (t == null) {
            throw BusinessException.build(SatelliteConstant.Exception.RECORD_NOT_EXIST);
        }
        processBeforeOperation(t, BaseOperationEnum.DELETE);
        Integer result = getMapper().deleteById(id);

        if (result == null || result < 1) {
            throw BusinessException.build(SatelliteConstant.Exception.DELETE_FAILURE);
        }
        refreshCacheFields(t, BaseOperationEnum.DELETE);

        processAfterOperation(t, BaseOperationEnum.DELETE);

        return result;
    }

    /**
     * 批量删除
     *
     * @param idList id列表
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean deleteByIds(Collection<? extends Serializable> idList) {
        List<T> list = getMapper().selectBatchIds(idList);
        //操作前处理
        processBatchBeforeOperation(list, BaseOperationEnum.DELETE_BATCH);

        boolean result = SqlHelper.retBool(getMapper().deleteBatchIds(idList));

        if (result) {
            //缓存
            list.forEach(t -> refreshCacheFields(t, BaseOperationEnum.DELETE_BATCH));
            //操作后处理
            processBatchAfterOperation(list, BaseOperationEnum.DELETE_BATCH);
        }

        return result;
    }

    /**
     * 根据条件删除
     */
    @Transactional(rollbackFor = Exception.class)
    default void delete(LambdaQueryWrapper<T> lambdaQueryWrapper) {
        getMapper().delete(lambdaQueryWrapper);
    }

    /**
     * 查询符合条件的数量
     *
     * @param wrapper
     * @return
     */
    default Integer count(Wrapper<T> wrapper) {
        return getMapper().selectCount(wrapper);
    }

    /**
     * 查询符合条件的记录
     *
     * @param wrapper
     * @return
     */
    default List<T> list(Wrapper<T> wrapper) {
        return getMapper().selectList(wrapper);
    }

    /**
     * 查询所有
     *
     * @return
     */
    default List<T> findAll() {
        return getMapper().selectList(Wrappers.emptyWrapper());
    }

    /**
     * 通过id查询
     *
     * @param id
     * @return
     */
    default T selectById(Serializable id) {
        return wrapperEntity(getMapper().selectById(id));
    }

    /**
     * 根据条件查询
     *
     * @param wrapper 条件
     * @return
     */
    default T selectOne(Wrapper<T> wrapper) {
        return wrapperEntity(getMapper().selectOne(wrapper));
    }

    //
//    default T selectOne(String column, Object value) {
//        List<T> list = list(column, value);
//        if (list.isEmpty()) {
//            return null;
//        }
//        return list.get(0);
//    }
//
//    /**
//     * 查询指定值的数据
//     *
//     * @param column 列
//     * @param value  值
//     * @return
//     */
//    default List<T> list(String column, Object value) {
//        QueryWrapper<T> queryWrapper = new QueryWrapper();
//        queryWrapper.eq(column, value);
//        return getMapper().selectList(queryWrapper);
//    }

    /**
     * 分页查询所有
     *
     * @param pageParam
     * @return
     */
    default IPage<T> page(P pageParam) {
        return page(pageParam, null);
    }

    /**
     * 分页查询，带查询条件
     *
     * @param pageParam 分页参数
     * @param wrapper   查询条件
     * @return
     */
    default IPage<T> page(P pageParam, Wrapper<T> wrapper) {

        Page<T> page = new Page<>();
        page.setCurrent(pageParam.getPageNumber());
        page.setSize(pageParam.getPageSize());

        //设置排序(排序升级，支持多字段多种排序)
//        order(page, pageParam);
        orderNew(page, pageParam);

        //有wrapper自定义查询条件,就不带参数的条件
        if (wrapper != null) {
            return resultHandler(getMapper().selectPage(page, wrapper));
        }

        Wrapper<T> queryWrapper = extensionWrapper(pageParam, getWrapper(pageParam));

        //扩展：对条件进行处理
        handlerQueryWrapper(queryWrapper);

        return resultHandler(getMapper().selectPage(page, queryWrapper));
    }

    /**
     * todo 待实现,是否需要做缓存及缓存刷新
     *
     * @param entity
     * @param operation
     */
    default void refreshCacheFields(T entity, BaseOperationEnum operation) {
    }

    /**
     * 检验唯一
     *
     * <p>
     * 唯一索引
     * {@see Unique}
     * </p>
     *
     * <p>
     * 多字段联合索引
     * {@see UnionUnique}
     * </p>
     *
     * @param entity   实体类
     * @param isUpdate 是否更新
     */
    default void checkUniqueField(T entity, boolean isUpdate) {
        //得到该类及其父类所声明的字段
        Field[] allFields = ReflectUtil.getFields(entity.getClass());
        //主键id字段
        Optional<Field> idFiledOptional = Arrays.stream(allFields)
                .filter(f -> f.isAnnotationPresent(TableId.class))
                .findFirst();
        //主键id不存在,忽略校验
        if (!idFiledOptional.isPresent()) {
            return;
        }

        Field idField = idFiledOptional.get();
        idField.setAccessible(true);


        //唯一索引 验证
        for (Field field : allFields) {
            if (field.isAnnotationPresent(Unique.class)) {
                Integer result;

                Unique unique = field.getDeclaredAnnotation(Unique.class);
                try {
                    Object value = ReflectUtil.getFieldValue(entity, field);
                    //如果没有指定column，则默认是实体字段的驼峰转下划线作为列
                    String column;
                    if (StringUtils.isBlank(unique.column())) {
                        //列：驼峰转下划线
                        column = SatelliteUtils.camelCaseToUnderline(field.getName());
                    } else {
                        column = unique.column();
                    }

                    QueryWrapper<T> queryWrapper = Wrappers.query();
                    queryWrapper.eq(column, value);
                    if (isUpdate) {
                        //如果是更新，排除自己的这条数据
                        queryWrapper.ne(idField.getAnnotation(TableId.class).value(),
                                ReflectUtil.getFieldValue(entity, idField));
                    }
                    result = getMapper().selectCount(queryWrapper);
                } catch (Exception e) {
                    //如果出错，继续,不打印异常，会在数据库层面体现
                    continue;
                }
                if (result > 0) {
                    //说明存在一条数据，报出异常
                    throw BusinessException.build(unique.code(), field.getName());
                }
            }
        }


        //联合索引 验证
        Map<String, QueryWrapper<T>> unionUniqueMap = new HashMap<>();
        //将查询条件放在unionUniqueMap中
        for (Field field : allFields) {
            if (field.isAnnotationPresent(UnionUnique.class)) {
                try {
                    //这样写，可以待注解扩展为可重复
                    UnionUnique[] unionUniques = field.getDeclaredAnnotationsByType(UnionUnique.class);
                    for (UnionUnique uu : unionUniques) {
                        String group = uu.group();
                        Object value = ReflectUtil.getFieldValue(entity, field);
                        String column;
                        if (StringUtils.isBlank(uu.column())) {
                            column = SatelliteUtils.camelCaseToUnderline(field.getName());
                        } else {
                            column = uu.column();
                        }
                        if (unionUniqueMap.containsKey(group)) {
                            QueryWrapper<T> queryWrapper = unionUniqueMap.get(group);
                            queryWrapper.eq(column, value);
                        } else {
                            QueryWrapper<T> queryWrapper = Wrappers.query();
                            queryWrapper.eq(column, value);
                            unionUniqueMap.put(group, queryWrapper);
                        }
                    }
                } catch (Exception e) {
                    //如果出错，继续,不打印异常，会在数据库层面体现
                    continue;
                }
            }
        }
        //验证联合索引的结果
        if (CollectionUtils.isEmpty(unionUniqueMap)) {
            return;
        }
        Set<Map.Entry<String, QueryWrapper<T>>> entries = unionUniqueMap.entrySet();
        for (Map.Entry<String, QueryWrapper<T>> entry : entries) {
            QueryWrapper<T> queryWrapper = entry.getValue();
            if (isUpdate) {
                //如果是更新排除自己
                try {
                    queryWrapper.ne(idField.getAnnotation(TableId.class).value(), idField.get(entity));
                } catch (IllegalAccessException ignore) {
                }
            }
            Integer result = getMapper().selectCount(queryWrapper);
            if (result > 0) {
                //错误信息
                String msg = entity.getClass().getName() +
                        " " +
                        entry.getKey() +
                        " " +
                        "unionUnique error";
                throw BusinessException.build(msg);
            }
        }

    }


    /**
     * 设置排序
     *
     * @param page      设置排序
     * @param pageParam 查询条件实体
     *                  order：升序还是降序
     *                  sort：排序的字段
     */
    default void order(Page<T> page, P pageParam) {
        String sort = pageParam.getSort();

        if (StringUtils.equals(SatelliteConstant.ASC, pageParam.getOrder())
                && StringUtils.isNotEmpty(pageParam.getSort())) {
            if (sort.contains(SatelliteConstant.SPLIT)) {
                page.addOrder(OrderItem.ascs(sort.split(SatelliteConstant.SPLIT)));
            } else {
                page.addOrder(OrderItem.asc(sort));
            }
        } else if (StringUtils.equals(SatelliteConstant.DESC, pageParam.getOrder())
                && StringUtils.isNotEmpty(pageParam.getSort())) {
            if (sort.contains(SatelliteConstant.SPLIT)) {
                page.addOrder(OrderItem.descs(sort.split(SatelliteConstant.SPLIT)));
            } else {
                page.addOrder(OrderItem.desc(sort));
            }
        }
    }

    /**
     * 设置排序(版本二)
     *
     * @param page      设置排序
     * @param pageParam 查询条件实体
     */
    default void orderNew(Page<T> page, P pageParam) {
        String order = pageParam.getOrder();
        String sort = pageParam.getSort();

        String[] sortSplit;
        String[] orderSplit;

        if (StringUtils.isNotBlank(sort) && StringUtils.isNotBlank(order)) {

            /**
             * 多字段排序逻辑：
             * 都含有分隔符 "," 逗号时：
             * 如果sort字段和order字段一一对应，则一一对应放入orderItem中
             * 如果不对应，则以排序字段order中的第一个排序为准
             */
            if (sort.contains(SatelliteConstant.SPLIT) && order.contains(SatelliteConstant.SPLIT)) {
                sortSplit = sort.split(SatelliteConstant.SPLIT);
                orderSplit = order.split(SatelliteConstant.SPLIT);

                OrderItem[] orderItems = new OrderItem[sortSplit.length];
                if (sortSplit.length == orderSplit.length) {
                    //多字段的排序，sort和order的分割长度 如果一致，则可以多字段按需排序
                    for (int i = 0; i < orderSplit.length; i++) {
                        orderItems[i] = buildOrder(
                                SatelliteUtils.camelCaseToUnderline(sortSplit[i]), orderSplit[i]);
                    }
                } else {
                    //否则按照order中的第一个排序来排序
                    String order0 = orderSplit[0];
                    for (int i = 0; i < sortSplit.length; i++) {
                        orderItems[i] = buildOrder(
                                SatelliteUtils.camelCaseToUnderline(sortSplit[i]), order0);
                    }
                }
                page.addOrder(orderItems);
            }
            //当只有排序字段sort有分隔符时
            if (sort.contains(SatelliteConstant.SPLIT) && !order.contains(SatelliteConstant.SPLIT)) {
                List<String> orderList = Arrays.stream(sort.split(SatelliteConstant.SPLIT))
                        .map(SatelliteUtils::camelCaseToUnderline)
                        .collect(Collectors.toList());
                String[] orderColumns = orderList.toArray(new String[]{});

                if (SatelliteConstant.DESC.equalsIgnoreCase(order)) {
                    page.addOrder(OrderItem.descs(orderColumns));
                } else {
                    //默认也是升序
                    page.addOrder(OrderItem.ascs(orderColumns));
                }

            }

            //都没有分隔符时
            if (!sort.contains(SatelliteConstant.SPLIT) && !order.contains(SatelliteConstant.SPLIT)) {
                page.addOrder(buildOrder(SatelliteUtils.camelCaseToUnderline(sort), order));
            }

        }
    }

    /**
     * 构建排序
     *
     * @param column 列
     * @param order  排序
     *               只有order=desc/DESC才是降序，其他默认都是正序
     * @return
     */
    default OrderItem buildOrder(String column, String order) {
        OrderItem orderItem = new OrderItem();
        orderItem.setColumn(column);
        //默认是正序
        if (SatelliteConstant.DESC.equalsIgnoreCase(order)) {
            orderItem.setAsc(false);
        }
        return orderItem;
    }

}
